require 'dgo/table/table'
require 'gadgets/scroll_box'
require 'gadgets/frame'
require 'map_editor/tile_panel'

module MapEditor
  class MapPanel < TilePanel
    include DGO::Table
    ZOOMS = [0.5, 1, 2, 3]
    
    attr_reader :panel_mode, :current_palet
    attr_reader :map, :show_layers, :current_layer_no
    attr_accessor :map_id, :memory, :current_palet
    
    def initialize(model, zoom_index, controlers, default_controler, current_layer_no, show_layers)
      @zoom_index = zoom_index
      super(ZOOMS[@zoom_index])
      @model = model

      @controlers = controlers
      @controlers.each do |key, controler|
        controler.target = self
      end
      
      self.controler = @controlers[default_controler]
      
      @show_layers = show_layers
      
      @scroll_box = Editor::ScrollBox.new(@model.tile_x_count * self.zoomed_grid_width, @model.tile_y_count * self.zoomed_grid_height, self.zoomed_grid_width, self.zoomed_grid_height) do |type|
        @sx = -1
        @sy = -1
        @ex = -1
        @ey = -1

        case type
          when "resize":
            @model.map.set_show_size(@scroll_box.x_count, @scroll_box.y_count, @model.layers)
            @invalidate_area = {:sx => -1, :sy => -1, :x_count => -1, :y_count => -1}
            self.render
          when "render"
            @invalidate_area = {:sx => -1, :sy => -1, :x_count => -1, :y_count => -1}
            self.render
        end
      end
      
      @psx = -1
      @psy = -1
      @pex = -1
      @pey = -1

      @pframe_width = -1
      @pframe_height = -1

      @current_layer_no = current_layer_no
      
      # self.set_panel
      self.set_panel
      self.set_signals
      self.invalidate(false)
    end
    
    def set_map_size(x_count, y_count)
      unless map.x_count == x_count && map.y_count == y_count
        @model.set_map_size(x_count, y_count)
        @scroll_box.set_client_size(x_count * self.zoomed_grid_width, y_count * self.zoomed_grid_height)
        @scroll_box.refresh_scrollbars
        @scroll_box.on_resize
      end
    end
    
    def show_layers=(value)
      return unless @show_layers != value
      @show_layers = value
      self.invalidate(false)
    end
    
    def panel_mode=(value)
      return if @panel_mode == value

      @panel_mode = value
      
      self.controler = @controlers[@panel_mode]

      self.invalidate(false)
    end

    # Property 
    def current_layer_no=(value)
      @current_layer_no = value
      self.render
    end
    
    def current_layer
      return @model.layers[@current_layer_no]
    end

    def map
      return @model.map
    end
    
    def grid_width
      @model.grid_width
    end
    
    def grid_height
      @model.grid_height
    end

    def zoom_index=(value)
      @zoom_index = value
      self.zoom = ZOOMS[@zoom_index]
    end

    def update_panel
      @model.map.base_x = self.scroll_x / self.zoom
      @model.map.base_y = self.scroll_y  / self.zoom

      if @texture.nil? || (@texture.width != @model.map.show_width || @texture.height != @model.map.show_height)
        if(@model.map.show_width != 0 && @model.map.show_height != 0)
          @texture = Texture.new(@model.map.show_width, @model.map.show_height)
        end
      end
      @texture.fill(Color.new(128, 128, 128, 255))
      
      @model.map.update(@model.layers)
      
      if @model.panorama
        @model.panorama.render(@texture)
      end
      
      @model.layers.each_with_index do |layer, i|
        tone_red = 0
        tone_green = 0
        tone_blue= 0
        alpha = 255
        t = @current_layer_no - i
        
        if @show_layers
          if t > 0
            tone_red = -80 * t 
            tone_green = -80 * t
            tone_blue = -80 * t
          else
            alpha = 255 + 80 * t
          end
        end
 
        @model.map.render(@texture, i, 0, 0, :tone_red => tone_red, :tone_green => tone_green, :tone_blue => tone_blue, :alpha => alpha)
      end 
      self.controler.render(@texture)

      tw = @invalidate_area[:x_count] < 0 ? @scroll_box.width : @invalidate_area[:x_count] * self.zoomed_grid_width
      th = @invalidate_area[:y_count] < 0 ? @scroll_box.height : @invalidate_area[:y_count] * self.zoomed_grid_height

      if(@dst_texture.nil? || (@dst_texture.width != tw || @dst_texture.height != th))
        @dst_texture = Texture.new(tw, th)
      end
      
      tx = @invalidate_area[:sx] < 0 ? 0 : [(@invalidate_area[:sx] - self.scroll_x_count) * @model.grid_width, 0].max
      ty = @invalidate_area[:sy] < 0 ? 0 : [(@invalidate_area[:sy] - self.scroll_y_count) * @model.grid_height, 0].max
      
       if(tx < @texture.width && ty < @texture.height)
        @dst_texture.render_texture(@texture, 0, 0, :scale_x => self.zoom, :scale_y => self.zoom, :src_x => tx, :src_y => ty, :src_width => [tw / self.zoom, @texture.width - tx].min, :src_height => [th / self.zoom, @texture.height - ty].min)
       end
    end
    
    def render
      self.update_panel
      area = @scroll_box.content_image
      
      return if area.window.nil?

      dst_x = @invalidate_area[:sx] < 0 ? 0 : (@invalidate_area[:sx] - self.scroll_x_count) * self.zoomed_grid_width
      dst_y = @invalidate_area[:sy] < 0 ? 0 : (@invalidate_area[:sy] - self.scroll_y_count) * self.zoomed_grid_height

      tw = @dst_texture.width
      th = @dst_texture.height

      buf = Gdk::Pixbuf.new(@dst_texture.dump('rgb'), Gdk::Pixbuf::ColorSpace.new(Gdk::Pixbuf::ColorSpace::RGB), false, 8, tw, th, tw * 3)
      gc = Gdk::GC.new(area.window) 
      area.window.draw_pixbuf(gc, buf, 0, 0, dst_x, dst_y, buf.width, buf.height, Gdk::RGB::DITHER_NONE, 0, 0)

      @psx = @sx
      @psy = @sy
      @pex = @ex
      @pey = @ey
      @pframe_width = @frame.width
      @pframe_height = @frame.height

      @invalidate_area = {:sx => -1, :sy => -1, :x_count => -1, :y_count => -1}
    end

    def invalidate(auto, tx = 0, ty =0, tw = 0, th = 0)
      if(auto)
        if((@sx != @psx) || (@sy != @psy) || (@ex != @pex) || (@ey != @pey) || (@pframe_width != @frame.width) || (@pframe_height != @frame.height))
          @psx = @sx if(@psx < 0)
          @psy = @sy if(@psy < 0)
          @pex = @ex if(@pex < 0)
          @pey = @ey if(@pey < 0)

          sx = [@sx, @ex].min
          sy = [@sy, @ey].min
          sx = [sx + tx, self.scroll_x_count].max
          sy = [sy + ty, self.scroll_y_count].max
          
          psx = [@psx, @pex].min
          psy = [@psy, @pey].min
          psx = [psx + tx, self.scroll_x_count].max
          psy = [psy + ty, self.scroll_y_count].max
          
          tsx = [psx, sx].min
          tsy = [psy, sy].min          
          tex = tw + [psx + @pframe_width, sx + @frame.width].max
          tey = th + [psy + @pframe_height, sy + @frame.height].max
          tex = [tex, self.right_count].min
          tey = [tey, self.bottom_count].min
          
          tsw = tex - tsx
          tsh = tey - tsy

          if(tsx >= 0 && tsy >= 0 && tsw >= 1 && tsh >= 1)
            @invalidate_area = {:sx => tsx, :sy => tsy, :x_count => tsw, :y_count => tsh}
            self.render
          end
        end
      else
        @invalidate_area = {:sx => -1, :sy => -1, :x_count => -1, :y_count => -1}
        self.render
      end
    end

    #Events
   
    def on_left_down(event)
      @psx = -1
      @psy = -1
      @sx, @sy = get_abs_location(event.x, event.y)
      @ex, @ey = @sx, @sy
      super(event)
    end

    def on_left_up(event)
      super(event)
    end
    
    def on_motion(event)      
      super(event)
    end
          
    def on_right_down(event)     
      super(event)
    end

    def on_right_up(event)
      super(event)
    end

    def on_resize(width, height)
      @scroll_box.set_size(width, height)
    end
  end
end